package org.kisekiproject.instance {
	import flash.display.BitmapData;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	
	import org.kisekiproject.evaluator.Expression;
	import org.kisekiproject.evaluator.IExpression;
	import org.kisekiproject.objectset.ObjectSet;
	import org.kisekiproject.utils.StringUtil;

	public class PropertyExpression extends EventDispatcher implements IExpression {

		private var _expression:Expression;
		private var _constantValue:Object;
		private var _instance:Instance;
		private var _arrayInstances:Array;
		private var _resourceName:String;
		private var _factory:InstanceFactory;
		private var _definition:PropertyDefinition;
		private var _resourceValue:Object;

		/**
		 * Create a property expression.
		 */
		public function PropertyExpression(def:PropertyDefinition, source:XML, factory:InstanceFactory) {
			_definition=def;
			_factory=factory;

			var s:String=StringUtil.trim(source.valueOf());

			if (s.charAt(0)=="{" && s.charAt(s.length-1)=="}") {
				var expStr:String=s.substr(1,s.length-2);
				//trace("creating prop expr: "+expStr);
				_expression=new Expression(expStr, factory.variables);
				_expression.addEventListener(Event.CHANGE, onExpressionChange);
			}

			else if (def.type=="String" || def.type=="Number" || def.type=="int" || def.type=="Boolean") {
				_constantValue=source.valueOf();
			}

			else if (def.type=="Array") {
				_arrayInstances=new Array();

				for each (var item:XML in source.children()) {
					var instance:Instance=factory.createInstanceFromXML(item);
					_arrayInstances.push(instance);
				}
			}

			else if (source.@src) {
				_resourceName=source.@src;
			}

			else {
				var l:XMLList=source.children();
				if (l.length()!=1)
					throw new Error("Expected object for property "+def.name);

				_instance=factory.createInstanceFromXML(l[0]);
			}
		}

		/**
		 * Expression change.
		 */
		protected function onExpressionChange(event:Event):void {
			dispatchEvent(new Event(Event.CHANGE));
		}

		/**
		 * Get value.
		 */
		public function get value():Object {
			if (_resourceName) {
				if (!_resourceValue) {
					trace("we have a resource: "+_resourceName+" type: "+_definition.type);

					trace("switching..");
					switch (_definition.type) {
						case "flash.display::Bitmap":
							_resourceValue=_factory.resources.getBitmap(_resourceName);
							break;

						case "flash.display::MovieClip":
							_resourceValue=_factory.resources.getMovieClip(_resourceName);
							break;

						case "flash.media::Sound":
							_resourceValue=_factory.resources.getSound(_resourceName);
							break;

						default:
							throw new Error("Don't know how to load a resource for type: "+_definition.type);
							break;
					}
				}

				return _resourceValue;
			}

			else if (_arrayInstances) {
				var a:Array=new Array();

				for each (var i:Instance in _arrayInstances)
					a.push(i.object);

				return a;
			}

			else if (_expression) {
				var v:Object=_expression.value;

				//trace("getting value from prop expr..."+v);
				if (v is ObjectSet) {
					//trace("yep, it's an object set");
					v=ObjectSet(v).array;
				}

				return v;
			}

			else if (_instance)
				return _instance.object;

			return _constantValue;
		}

		/**
		 * Get required resources.
		 */
		public function get requiredResources():Array {
			if (_resourceName) {
				return [_resourceName];
			}

			else if (_arrayInstances) {
				var a:Array=new Array();

				for each (var i:Instance in _arrayInstances)
					a=a.concat(i.requiredResources);

				return a;
			}

			else
				return [];
		}
	}
}